# Copyright (c) 2021-2023 Alibaba Cloud Computing Ltd.
# SPDX-License-Identifier: MulanPSL-2.0

import os  
import re
import json 
import logging

from collections import defaultdict
from abc import ABCMeta, abstractmethod
from agent.common.config import Config

logger = logging.getLogger('common')

class BaseDomain(metaclass=ABCMeta):
    def __init__(self, domain_name):
        self.domain_name = domain_name
    
    @abstractmethod
    def set_value(self, param_name:str, param_value):
        pass
    
    @abstractmethod
    def get_value(self, param_name:str):
        pass

    def vaild_param(self, param_name) -> bool:
        """ check if param is vaild

        Args:
            param_name (str): parameter name

        Returns:
            bool: _description_
        """
        return True
    
    def pre_setting(self):
        pass 

    def post_setting(self):
        pass
    
    def set_values(self, param_list: dict) -> dict:
        self.pre_setting()

        res = defaultdict(dict)
        for param_name, param_info in param_list.items():
            if not self.vaild_param(param_name):
                logger.warning("invalid param '{}:{}'".format(self.domain_name, param_name))
                res[param_name] = {"suc":False, "value": "", "msg":"Invalid param {}:{}".format(self.domain_name, param_name)}
                continue
            
            try:
                self.set_value(param_name = param_name, param_value = param_info['value'])
                logger.debug("setting '{}:{}' to {}".format(self.domain_name, param_name,  param_info['value']))
                res[param_name] = {"suc":True, "value": "", "msg":""}

            except Exception as e:
                logger.error("param '{}:{}' setting error: {}".format(self.domain_name, param_name, e))
                res[param_name] = {"suc":False, "value": "", "msg":"{}".format(e)}
        
        self.post_setting()
        return res

    def get_values(self, param_list: dict) -> dict:
        res = defaultdict(dict)
        for param_name, _ in param_list.items():
            if not self.vaild_param(param_name):
                logger.warning("invalid param {}:{} ".format(self.domain_name, param_name))
                res[param_name] = {
                    "suc":False, "value":None, "msg":"Invalid parameter"}
                continue
            
            try:
                value = self.get_value(param_name = param_name)
                res[param_name] = {
                    "suc":True, "value":value, "msg":""}
                
            except Exception as e:
                res[param_name] = {
                    "suc":False, "value": None, "msg":"param reading error: {}".format(e)}
        return res

    def backup(self, param_list):
        self.backup_file = os.path.join(Config.BACKUP_PATH, "{}.json".format(self.domain_name))
        
        try:
            self.rollback()
        except Exception as e:
            logger.error("'{}' rollback failed: {}".format(self.domain_name, e))
            raise
        
        backup_result = defaultdict(dict)
        backup_value = defaultdict(dict)
        for param_name in param_list.keys():
            try:
                value = self.get_value(param_name = param_name)
                backup_result[param_name] = {"suc": True, "msg": value}
                backup_value[param_name] = value

            except Exception as e:
                backup_result[param_name] = {"suc": False, "msg": "{}".format(e)}
                logger.error("'{}:{}' reading error: {}".format(self.domain_name, param_name, e))
                continue

        with open(self.backup_file, "w") as f:
            f.write(json.dumps(backup_value))
        return backup_result

    def rollback(self):
        self.backup_file = os.path.join(Config.BACKUP_PATH, "{}.json".format(self.domain_name))
        
        if not os.path.exists(self.backup_file):
            return
        
        logger.info("rollback from file: {}".format(self.backup_file))
        self.pre_setting()
        for param_name, value in json.load(open(self.backup_file)).items():
            try:
                self.set_value(param_name = param_name, param_value = value)
            except Exception as e:
                logger.error("setting '{}' to '{}' failed: {}".format(param_name, value, e))
                raise
        
        self.post_setting()
        os.remove(self.backup_file)


class BaseBenchmark(BaseDomain):
    __domain__ = "base_benchmark"
    
    def __init__(self, domain_name):
        super().__init__(domain_name = domain_name)
        if not os.path.exists("/etc/keentune/bench"):
            raise Exception("keentune-bench is not installed in this env.")
        
        if not os.path.exists("/var/keentune/bench/scripts"):
            raise Exception("The version of keentune-bench is not match with agent.")

        self.param_value = self.default_param
    
    def set_value(self, param_name:str, param_value):
        if param_value != None:
            self.param_value[param_name] = param_value
    
    def get_value(self, param_name:str):
        if self.param_value.__contains__(param_name):
            return self.param_value[param_name]
    
    def post_setting(self):
        args = " ".join(["-{}={}".format(k, v) for k,v in self.param_value.items()])
        with open(self.script_path, "r") as f:
            data = f.read()
            data = re.sub(r'DEFAULT = "[a-zA-Z0-9\-=\s]*"', 'DEFAULT = "{}"'.format(args), data)

        with open(self.script_path, "w") as f:
            f.write(data)
        self.param_value = self.default_param
